"""开发一个简单错误记录功能小模块，能够记录出错的代码所在的文件名称和行号。
处理：
1、 记录最多8条错误记录，循环记录（或者说最后只输出最后出现的八条错误记录），对相同的错误记录（净文件名（保留最后16位）称和行号完全匹配）只记录一条，错误计数增加；
2、 超过16个字符的文件名称，只记录文件的最后有效16个字符；
3、 输入的文件可能带路径，记录文件名称不能带路径。
输入描述:
一行或多行字符串。每行包括带路径文件名称，行号，以空格隔开。
输出描述:
将所有的记录统计并将结果输出，格式：文件名 代码行数 数目，一个空格隔开，如：
示例1
输入
E:\V1R2\product\fpgadrive.c   1325
输出
fpgadrive.c 1325 1"""

import time
import datetime
from pathlib import Path
from collections import namedtuple



class Logging:
    """工具包，可以支持不实例化，直接用快捷方法
    有悖于需求，因为如果只有文件名相同路径不同的时候无法区分，所以多增加了一个文件路径的显示
    """
    
    ERRORNUMBER = 8
    FILELENGTH = 16
    Key = namedtuple("Key", 'file line path')

    def __init__(self):
        """创建一个记录错误记录的字典"""
        self.errors = {}

    def logger(self, info: str):
        """先进行字符串的切分，获取到文件路径，及错误记录的行号
           判断，此文件路径是否存在，若存在，才记录，这个不知道
           算不算过分设计，可以取消，不存在就不记录了，记录下文
           件的具体名字，时间戳，用来创建key，如果文件的真实名字
           超过了要求，那就取文件的最后的几个，判断此key是否存在
           记录的字典中，如果存在则加一，不存在则创建，存进去的值
           是一个元组，元组的第一项才是记录，第二项是时间戳
        """
        _file, _, line = info.rpartition(" ")
        file = Path(_file)
        if file.exists():
            name = file.name
            now = datetime.datetime.now().timestamp()
            key = self.Key(file[-self.FILELENGTH:] if len(name) > self.FILELENGTH else name, line, _file)
            self.errors[key] = (self.errors.get(key, (0, now))[0] + 1, now)

    def show(self):
        """将errors字典中的错误日志进行排序后输出，按照时间戳进行排序"""
        for key, (count, _) in sorted(self.errors.items(), key=lambda x: x[1][1], reverse=True)[:self.ERRORNUMBER]:
            print(key.file, key.line, count, key.path)

    @property
    def _show(self):
        """保存了所有的数据记录，用此方法可以返回所有的记录，只不过没好好的进行显示"""
        return self.errors

    def pop(self):
        """先排序，默认留下最新的指定个数记录，其余的删掉"""
        pops = sorted(self.errors.items(), key=lambda x: x[1][1])[:-self.ERRORNUMBER]
        for k, _ in pops:
            self.errors.pop(k)

    def clear(self):
        """错误记录真正的删除，真删除"""
        self.errors.clear()
        
    # 以下内容是提供的快捷方法，后来发现，这些快捷方法可以放到模块上，那样会用的更方便，不知道具体的业务是啥，这样也行了
    @classmethod
    def logger2(cls, info: str):
        obj = getattr(cls, "_logger", None)
        if not obj:
            setattr(cls, "_logger", Logging())
            obj = getattr(cls, "_logger")
        obj.logger(info)

    @classmethod
    def _show2(cls):
        """保存了所有的数据记录，用此方法可以返回所有的记录，只不过没好好的进行显示"""
        return getattr(cls, "_logger").show().errors

    @classmethod
    def show2(cls):
        getattr(cls, "_logger").show()

    @classmethod
    def pop2(cls):
        getattr(cls, "_logger").pop()

    @classmethod
    def clear2(cls):
        getattr(cls, "_logger").errors.clear()


if __name__ == '__main__':
    logger = Logging()
    logger.logger("./t6.py 10")
    logger.logger("./t6.py 10")
    # logger.show()

    Logging.logger2("./t7.py 10")
    Logging.logger2("./test/t7.py 10")
    time.sleep(0.5)
    Logging.logger2("./t7.py 2")
    Logging.logger2("./t7.py 1")
    Logging.logger2("./t7.py 3")
    Logging.logger2("./t7.py 3")
    Logging.logger2("./t7.py 3")
    Logging.logger2("./t7.py 4")
    Logging.logger2("./t7.py 5")
    Logging.logger2("./t7.py 6")
    Logging.logger2("./t7.py 7")
    Logging.logger2("./t7.py 8")
    Logging.logger2("./t7.py 8")
    Logging.show2()
    Logging.clear2()
    print('~' * 50)

    Logging.show2()
# 眼花缭乱的